🚀 Proven Ways to Improve Spring Boot Applications
🟢 Performance Optimization (1-15)
1️⃣ Use Lazy Initialization
// application.properties
spring.main.lazy-initialization=true
Benefit: Reduces startup time by 20-40% by loading beans only when needed.
Trade-off: First request might be slower. Use selectively for large applications.
2️⃣ Enable HTTP/2
# application.yml
server:
http2:
enabled: true
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: secret
Benefit: Multiplexing, header compression, server push → faster page loads.
Impact: 30-50% improvement in page load times for complex UIs.
3️⃣ Optimize Database Connection Pool
@Configuration
public class DataSourceConfig {
@Bean
public HikariConfig hikariConfig() {
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(20); // Default: 10
config.setMinimumIdle(5); // Default: 10
config.setConnectionTimeout(30000); // 30 seconds
config.setIdleTimeout(600000); // 10 minutes
config.setMaxLifetime(1800000); // 30 minutes
config.setLeakDetectionThreshold(60000); // 1 minute
return config;
}
}
Benefit: Prevents connection exhaustion and improves response time.
Rule of Thumb: connections = ((core_count * 2) + effective_spindle_count)
4️⃣ Enable Database Query Caching
@Entity
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product {
@Id
private Long id;
private String name;
}
// application.properties
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.jcache.JCacheRegionFactory
spring.cache.jcache.provider=org.ehcache.jsr107.EhcacheCachingProvider
Benefit: Reduces database roundtrips by 60-80% for read-heavy operations.
5️⃣ Use @Async for Non-blocking Operations
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
}
@Service
public class EmailService {
@Async
public CompletableFuture<Void> sendEmail(String to, String message) {
// Send email logic
return CompletableFuture.completedFuture(null);
}
}
Benefit: Improves throughput by not blocking request threads for long-running tasks.
Use Case: Email sending, report generation, external API calls.
6️⃣ Enable Response Compression
# application.yml
server:
compression:
enabled: true
mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json
min-response-size: 1024
Benefit: Reduces payload size by 70-90%, faster data transfer.
7️⃣ Implement Database Indexing Strategy
@Entity
@Table(indexes = {
@Index(name = "idx_email", columnList = "email"),
@Index(name = "idx_created_date", columnList = "createdDate"),
@Index(name = "idx_status_date", columnList = "status,createdDate")
})
public class User {
@Id
private Long id;
@Column(unique = true)
private String email;
private LocalDateTime createdDate;
private String status;
}
Benefit: Query performance improvement from seconds to milliseconds.
Warning: Don't over-index; each index adds overhead to INSERT/UPDATE operations.
8️⃣ Use Pagination for Large Datasets
@RestController
public class ProductController {
@GetMapping("/products")
public Page<Product> getProducts(
@PageableDefault(size = 20, sort = "id") Pageable pageable) {
return productRepository.findAll(pageable);
}
}
// Usage: /products?page=0&size=20&sort=name,asc
Benefit: Prevents memory overflow and improves response times.
Best Practice: Default page size should be 20-50 items.
9️⃣ Implement DTO Pattern to Avoid N+1 Queries
// Bad - N+1 Query Problem
@Entity
public class Order {
@Id
private Long id;
@OneToMany(mappedBy = "order")
private List<OrderItem> items; // Lazy loading causes N+1
}
// Good - Use DTO with JOIN FETCH
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.id = :id")
Optional<Order> findByIdWithItems(@Param("id") Long id);
// Or use DTO projection
public interface OrderSummary {
Long getId();
String getCustomerName();
@Value("#{target.items.size()}")
Integer getItemCount();
}
Benefit: Reduces queries from N+1 to 1, massive performance gain.
🔟 Enable Actuator for Monitoring
# application.yml
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus,info
metrics:
export:
prometheus:
enabled: true
endpoint:
health:
show-details: when-authorized
Benefit: Real-time monitoring, performance metrics, health checks.
Tools: Integrate with Prometheus + Grafana for visualization.
1️⃣1️⃣ Use @Transactional Properly
@Service
public class OrderService {
// Good - Transactional on service layer
@Transactional
public void createOrder(OrderRequest request) {
Order order = orderRepository.save(new Order());
orderItemRepository.saveAll(order.getItems());
notificationService.sendConfirmation(order); // Should be async
}
// Better - Read-only for queries
@Transactional(readOnly = true)
public List<Order> findRecentOrders() {
return orderRepository.findTop10ByOrderByCreatedDateDesc();
}
}
Benefit: readOnly=true optimizes Hibernate's dirty checking and improves performance.
1️⃣2️⃣ Implement Circuit Breaker Pattern
@Configuration
public class ResilienceConfig {
@Bean
public CircuitBreaker circuitBreaker() {
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofSeconds(30))
.slidingWindowSize(10)
.build();
return CircuitBreaker.of("externalService", config);
}
}
@Service
public class ExternalService {
private final CircuitBreaker circuitBreaker;
@CircuitBreaker(name = "externalService", fallbackMethod = "fallback")
public String callExternalAPI() {
// External API call
return restTemplate.getForObject("https://api.example.com/data", String.class);
}
public String fallback(Exception e) {
return "Fallback response";
}
}
Benefit: Prevents cascading failures, improves system resilience.
Dependency: Spring Cloud Circuit Breaker / Resilience4j
1️⃣3️⃣ Use Batch Processing for Bulk Operations
@Service
public class UserService {
@Transactional
public void importUsers(List<User> users) {
int batchSize = 100;
for (int i = 0; i < users.size(); i++) {
userRepository.save(users.get(i));
if (i % batchSize == 0 && i > 0) {
entityManager.flush();
entityManager.clear();
}
}
}
}
// application.properties
spring.jpa.properties.hibernate.jdbc.batch_size=100
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
Benefit: 10-50x faster for bulk inserts/updates.
1️⃣4️⃣ Optimize Jackson Serialization
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
}
}
// Use @JsonView for selective serialization
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
@Entity
public class User {
@JsonView(Views.Public.class)
private String name;
@JsonView(Views.Internal.class)
private String email;
}
Benefit: Reduces payload size, improves serialization performance.
1️⃣5️⃣ Use Native Queries for Complex Operations
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
@Query(value = """
SELECT o.*, COUNT(oi.id) as item_count
FROM orders o
LEFT JOIN order_items oi ON o.id = oi.order_id
WHERE o.status = :status
GROUP BY o.id
HAVING COUNT(oi.id) > :minItems
""", nativeQuery = true)
List<Order> findOrdersWithMinItems(
@Param("status") String status,
@Param("minItems") int minItems
);
}
Benefit: Better performance for complex queries vs. JPQL/Criteria API.
Warning: Database-specific, less portable.
🟡 Security Best Practices (16-28)
1️⃣6️⃣ Implement JWT Token Authentication
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
Benefit: Stateless authentication, scalable, mobile-friendly.
1️⃣7️⃣ Enable HTTPS/SSL
# application.yml
server:
port: 8443
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: ${SSL_PASSWORD}
key-store-type: PKCS12
key-alias: tomcat
Benefit: Data encryption in transit, prevents MITM attacks.
Production: Use Let's Encrypt or proper CA-signed certificates.
1️⃣8️⃣ Implement Rate Limiting
@Configuration
public class RateLimitConfig {
@Bean
public RateLimiter rateLimiter() {
RateLimiterConfig config = RateLimiterConfig.custom()
.limitForPeriod(10)
.limitRefreshPeriod(Duration.ofSeconds(1))
.timeoutDuration(Duration.ofMillis(500))
.build();
return RateLimiter.of("api", config);
}
}
@RestController
public class ApiController {
@GetMapping("/api/data")
@RateLimiter(name = "api")
public ResponseEntity<String> getData() {
return ResponseEntity.ok("Data");
}
}
Benefit: Prevents abuse, DDoS protection, ensures fair usage.
1️⃣9️⃣ Use Environment Variables for Secrets
// application.yml
spring:
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jwt:
secret: ${JWT_SECRET}
expiration: ${JWT_EXPIRATION:3600000}
Benefit: Never commit secrets to version control.
Tools: Use Spring Cloud Config, Vault, AWS Secrets Manager.
2️⃣0️⃣ Implement CORS Properly
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://yourdomain.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
};
}
}
Benefit: Controlled cross-origin access, prevents unauthorized domains.
2️⃣1️⃣ Enable Security Headers
@Configuration
public class SecurityHeadersConfig {
@Bean
public SecurityFilterChain securityHeaders(HttpSecurity http) throws Exception {
http.headers(headers -> headers
.contentSecurityPolicy(csp -> csp
.policyDirectives("default-src 'self'; script-src 'self' 'unsafe-inline'")
)
.frameOptions(frame -> frame.deny())
.xssProtection(xss -> xss.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK))
.httpStrictTransportSecurity(hsts -> hsts
.includeSubDomains(true)
.maxAgeInSeconds(31536000)
)
);
return http.build();
}
}
Benefit: Protects against XSS, clickjacking, and other attacks.
2️⃣2️⃣ Validate Input Data
public class UserRequest {
@NotBlank(message = "Name is required")
@Size(min = 2, max = 50)
private String name;
@Email(message = "Invalid email format")
@NotBlank
private String email;
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{8,}$",
message = "Password must meet complexity requirements")
private String password;
}
@RestController
public class UserController {
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody UserRequest request) {
// Process request
return ResponseEntity.ok(user);
}
}
Benefit: Prevents SQL injection, XSS, and malformed data.
2️⃣3️⃣ Implement API Versioning
@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 {
@GetMapping
public List<UserDto> getUsers() {
return userService.findAll();
}
}
@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 {
@GetMapping
public Page<UserDtoV2> getUsers(Pageable pageable) {
return userService.findAll(pageable);
}
}
Benefit: Backward compatibility, smooth migration for API consumers.
2️⃣4️⃣ Enable Method-Level Security
@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {
}
@Service
public class UserService {
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
@PreAuthorize("hasRole('USER') and #userId == authentication.principal.id")
public User updateProfile(Long userId, UserRequest request) {
return userRepository.save(user);
}
}
Benefit: Fine-grained access control at method level.
2️⃣5️⃣ Implement Audit Logging
@Configuration
@EnableJpaAuditing
public class AuditConfig {
@Bean
public AuditorAware<String> auditorProvider() {
return () -> Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getName);
}
}
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Order {
@Id
private Long id;
@CreatedBy
private String createdBy;
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedBy
private String lastModifiedBy;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
Benefit: Track who did what and when, compliance requirements.
2️⃣6️⃣ Use Password Encoding
@Configuration
public class PasswordConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12); // Strength: 12
}
}
@Service
public class AuthService {
public void registerUser(UserRequest request) {
String encodedPassword = passwordEncoder.encode(request.getPassword());
User user = new User(request.getEmail(), encodedPassword);
userRepository.save(user);
}
public boolean validatePassword(String rawPassword, String encodedPassword) {
return passwordEncoder.matches(rawPassword, encodedPassword);
}
}
Benefit: Never store plain text passwords, protects against data breaches.
2️⃣7️⃣ Implement CSRF Protection for State-changing Operations
@Configuration
public class CsrfConfig {
@Bean
public SecurityFilterChain csrfProtection(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers("/api/public/**")
);
return http.build();
}
}
Benefit: Prevents cross-site request forgery attacks.
Note: Not needed for stateless JWT APIs.
2️⃣8️⃣ Sanitize User Input
@Component
public class InputSanitizer {
public String sanitizeHtml(String input) {
if (input == null) return null;
return Jsoup.clean(input, Safelist.basic());
}
public String sanitizeSql(String input) {
if (input == null) return null;
return input.replaceAll("[';\"\\-\\-]", "");
}
}
@RestController
public class CommentController {
@PostMapping("/comments")
public Comment createComment(@RequestBody CommentRequest request) {
String sanitizedContent = sanitizer.sanitizeHtml(request.getContent());
Comment comment = new Comment(sanitizedContent);
return commentRepository.save(comment);
}
}
Benefit: Prevents XSS and SQL injection attacks.